#pragma once

#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <ctime>
#include <cstdio>
#include "Mutex.hpp"

namespace LogModule
{
    
    //基类 -- 日志的刷新策略
    class LogStrategy
    {
    public:
        virtual void SyncLog(const std::string& message) = 0;
    };

    //派生类 -- 显示器刷新
    class ScreenStratrgy : public LogStrategy
    {
    public:
        //写入
        virtual void SyncLog(const std::string& message) override
        {
            _lock.lock();
            std::cout << message << std::endl;
            _lock.unlock();
        }
    private:
        //多个线程往显示器刷新数据的过程应该是互斥的，所以要加锁
        Mutex _lock;
    };

    //派生类 -- 文件刷新
    class FileStratrgy : public LogStrategy
    {
    public:
        FileStratrgy(const std::string& path = "./log", const std::string& file = "a.log")
            :_path(path)
            , _file(file)
        {
            _lock.lock();
            //判断路径是否存在，若不存在则创建
            if(std::filesystem::exists(_path))
            {
                _lock.unlock();
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch(const std::filesystem::filesystem_error& e)
            {
                std::cerr << e.what() << std::endl;
            }
        }

        //写入
        virtual void SyncLog(const std::string& message) override
        {
            _lock.lock();
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream out(filename, std::ios::app);
            out << message << "\r\n";
            out.close();
            _lock.unlock();
        }
    private:
        std::string _path;
        std::string _file;
        Mutex _lock;
    };

    //形成日志并能根据不同的策略刷新

    //日志等级
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARING,
        ERROR,
        FATAL
    };

    //获取当前时间
    std::string GetTime()
    {
        time_t cur = time(nullptr);
        struct tm cur_time;
        localtime_r(&cur, &cur_time);
        char buff[128] = {0};

        //注意：年份是减去1900的结果；月份是从0月到11月的
        snprintf(buff, sizeof buff, "%d-%02d-%02d %02d:%02d:%02d", cur_time.tm_year + 1900, cur_time.tm_mon + 1, cur_time.tm_mday, 
            cur_time.tm_hour, cur_time.tm_min, cur_time.tm_sec);
        return buff;
    }

    //将日志等级转化为字符串
    std::string level2str(const LogLevel& level)
    {
        switch (level)
        {
            case LogLevel::DEBUG: return "DEBUG";
            case LogLevel::INFO: return "INFO";
            case LogLevel::WARING: return "WARING";
            case LogLevel::ERROR: return "ERROR";
            case LogLevel::FATAL: return "FATAL";
            default: return "UNKNOWN";
        }
    }

    class Log
    {
    public:
        Log()
        {
            _strategy = std::make_unique<ScreenStratrgy>();
        }

        void EnableScreenStratrgy()
        {
            _strategy = std::make_unique<ScreenStratrgy>();
        }

        void EnableFileStratrgy()
        {
            _strategy = std::make_unique<FileStratrgy>();
        }

        //内部类，表示未来的一条日志
        //便于实现“可变参数”
        class LogMessage
        {
        public:
            LogMessage(const LogLevel& level, const std::string& src_name, int line, Log& logger)
                :_cur_time(GetTime())
                , _level(level)
                , _src_name(src_name)
                , _line(line)
                , _pid(getpid())
                , _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _cur_time << "]" <<
                    "[" << level2str(_level) << "]" <<
                    "[" << _pid << "]" <<
                    "[" << _src_name << "]" <<
                    "[" << _line << "]" << " - ";
                _loginfo = ss.str();
            }

            //析构时刷新
            ~LogMessage()
            {
                if(_logger._strategy)
                {
                    _logger._strategy->SyncLog(_loginfo);
                }
            }

            template<class T>
            LogMessage& operator<<(const T& info)
            {
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }
        private:
            std::string _cur_time;
            LogLevel _level;
            pid_t _pid;
            std::string _src_name;
            int _line;
            std::string _loginfo;
            Log& _logger;
        };

        LogMessage operator()(const LogLevel& level, const std::string& src_name, int line)
        {
            //先创建内部类的临时对象，等到可变参数全部写入_loginfo之后，析构时刷新数据
            return LogMessage(level, src_name, line, *this);
        }
    private:
        std::unique_ptr<LogStrategy> _strategy;
    };

    //创建全局对象，使用时直接调用其仿函数
    Log log;

    //使用宏简化用户操作
    #define Enable_Screen_Stratrgy() log.EnableScreenStratrgy()
    #define Enable_File_Stratrgy() log.EnableFileStratrgy()
    #define LOG(level) log(level, __FILE__, __LINE__)
}